/**
 * 单仓位交易策略模拟器
 *
 * 功能说明：
 * 1. 监控多个交易对的价格变化
 * 2. 当某个交易对的前一根K线涨幅超过触发阈值时，在下一根K线开盘时建仓
 * 3. 同时只能持有一个仓位，建仓后等待止盈或止损
 * 4. 支持优先交易对筛选
 * 5. 相同币种2次持仓之间时间不能低于10分钟
 * 6. 建仓条件：前一根K线涨幅 > gainTriggerPct 且收盘价与最高价差距跟开盘价不超过10%
 * 7. 连续亏损7次或以上时，使用2倍资金操作
 *
 * 策略逻辑：
 * - 建仓条件：前一根K线涨幅 > gainTriggerPct 且收盘价与最高价差距跟开盘价不超过10
 * - 止盈条件：价格 >= 建仓价 * (1 + takeProfitPct/100)
 * - 止损条件：价格 <= 建仓价 * (1 - stopLossPct/100)
 * - 冷却期：同一币种两次建仓间隔必须 >= 10分钟
 * - 资金管理：连续亏损7次或以上时，使用2倍资金操作
 */

/**
 * 计算涨幅百分比
 * @param {number} prevOpen - 前一根K线开盘价
 * @param {number} prevClose - 前一根K线收盘价
 * @returns {number} 涨幅百分比
 */
function calculateGainPct(prevOpen, prevClose) {
    return ((prevClose - prevOpen) / prevOpen) * 100;
  }

  /**
   * 计算收盘价与最高价的差距百分比
   * @param {number} prevClose - 前一根K线收盘价
   * @param {number} prevHigh - 前一根K线最高价
   * @returns {number} 差距百分比
   */
  function calculateCloseToHighPct(prevClose, prevHigh, prevOpen) {
    return Math.abs(((prevHigh - prevClose) / prevOpen) * 100);
  }

  let ordresWin = [];

  let scale2times = 0;
  // 获取仓位倍数
  function getScale(index) {
    // 结束奖励时间
    if (scale2times >= 10) {
      scale2times = 0;
      // 重新累计订单
      ordresWin = [];
    }
    // 奖励模式
    if (scale2times) {
      scale2times++;
      return 2;
    }
    if (ordresWin.length < 10) {
      return 1;
    }
    let sum = 0;
    ordresWin.map((i) => sum+= i);
    if (sum < 2) {
      scale2times++;
      return 2;
    }
    return 1;
  }

  // 记录每次的盈利情况
  function setOrderWin(index, win) {
    ordresWin.push(win);
    if (ordresWin.length > 10) {
      ordresWin.shift();
    }
  }
  // 连续亏损计数
  let consecutiveLosses = 0;
  // 交易次数
  let times = 0;
  /**
   * 模拟单仓位交易策略
   * @param {Array} klineData - 全部K线数据，包含多个交易对
   * @param {Object} config - 配置参数
   * @param {number} config.takeProfitPct - 止盈百分比
   * @param {number} config.stopLossPct - 止损百分比
   * @param {number} config.gainTriggerPct - 建仓触发涨幅百分比
   * @param {Array} config.prioritySymbols - 优先交易对列表，为空时交易所有对
   * @param {number} config.closeToHighPctTriggerPct - 收盘价与最高价差距触发百分比
   * @returns {Array} trades - 交易明细数组
   */
  function simulateSinglePositionStrategy(klineData, config) {
    const { takeProfitPct, stopLossPct, gainTriggerPct, prioritySymbols,  closeToHighPctTriggerPct, maxConsecutiveLosses} = config;

    // 按时间分组，将同一时间点的多个交易对数据归类
    const timeMap = new Map();
    for (const item of klineData) {
      if (!timeMap.has(item.open_time)) {
        timeMap.set(item.open_time, []);
      }
      timeMap.get(item.open_time).push(item);
    }

    // 按时间顺序排序
    const sortedTimes = Array.from(timeMap.keys()).sort((a, b) => a - b);
    // 记录每个交易对的最新K线数据
    const lastCandles = new Map();
    // 存储所有交易记录
    const trades = [];
    // 当前持仓状态，null表示无持仓
    let currentPosition = null;
    // 记录每个交易对的最后建仓时间（毫秒）
    const lastEntryTimes = new Map();
    // 冷却期时间：10分钟 = 10 * 60 * 1000 毫秒
    const cooldownPeriod = 10 * 60 * 1000;


    // 遍历每个时间点
    for (const time of sortedTimes) {
      const candles = timeMap.get(time);

      // 持仓处理：检查止盈止损条件
      if (currentPosition) {
        // 找到当前持仓交易对的数据
        const candle = candles.find(c => c.symbol === currentPosition.symbol);
        if (!candle) continue;
        const high = parseFloat(candle.high);
        const low = parseFloat(candle.low);
        // 检查止损条件：最低价达到止损价
        if (high >= currentPosition.takeProfit) {
          trades.push({
            ...currentPosition,
            exit_price: currentPosition.takeProfit,
            exit_time: time,
            result: "TP", // Take Profit
            profit: takeProfitPct * currentPosition.positionSize,
            positionSize: currentPosition.positionSize,
            times: currentPosition.times,
          });
          // 记录本次交易盈利
          setOrderWin(currentPosition.times, 0);
          currentPosition = null;
          // 重置连续亏损计数
          consecutiveLosses = 0;
        } else if (low <= currentPosition.stopLoss) {
          trades.push({
            ...currentPosition,
            exit_price: currentPosition.stopLoss,
            exit_time: time,
            result: "SL", // Stop Loss
            profit: -stopLossPct * currentPosition.positionSize,
            positionSize: currentPosition.positionSize,
            times: currentPosition.times,
          });
          // 记录本次交易亏损
          setOrderWin(currentPosition.times, 1);
          currentPosition = null;
          // 更新连续亏损计数
          consecutiveLosses++;
        // 检查止盈条件：最高价达到止盈价
        }
      }

      // 建仓处理：只在无持仓时寻找建仓机会
      if (!currentPosition) {
        for (const candle of candles) {
          // 如果设置了优先交易对，则只交易指定的交易对
          if (prioritySymbols.length && !prioritySymbols.includes(candle.symbol)) continue;

          // 检查冷却期：同一币种两次建仓间隔必须 >= 10分钟
          const lastEntryTime = lastEntryTimes.get(candle.symbol);
          if (lastEntryTime && (time - lastEntryTime) < cooldownPeriod) {
            continue; // 跳过仍在冷却期的交易对
          }

          // 获取该交易对的前一根K线数据
          const prev = lastCandles.get(candle.symbol);
          if (prev) {
            // 计算前一根K线的涨幅
            const gainPct = calculateGainPct(parseFloat(prev.open), parseFloat(prev.close));
            // 计算前一根K线收盘价与最高价的差距
            const closeToHighPct = calculateCloseToHighPct(parseFloat(prev.close), parseFloat(prev.low), parseFloat(prev.open));
            // 如果涨幅超过触发阈值且收盘价与最高价差距不超过1%，则建仓
            if (gainPct < -gainTriggerPct && closeToHighPct <= closeToHighPctTriggerPct) {
              const entry = parseFloat(candle.open);
              // 根据连续亏损次数决定仓位大小
              const positionSize = getScale(times) ;
              currentPosition = {
                symbol: candle.symbol,
                gainPct,
                entry_time: time,
                entry: entry,
                closeToHighPct,
                takeProfit: entry * (1 + takeProfitPct / 100), // 计算止盈价
                stopLoss: entry * (1 - stopLossPct / 100),      // 计算止损价
                positionSize: positionSize, // 记录仓位大小
                consecutiveLosses: consecutiveLosses,
                times: times,
              };
              times++
              // 记录该交易对的建仓时间
              lastEntryTimes.set(candle.symbol, time);
              break; // 建仓后退出循环，避免同时建多个仓位
            }
          }
        }
      }

      // 更新每个交易对的最新K线数据
      for (const candle of candles) {
        lastCandles.set(candle.symbol, candle);
      }
    }

    return trades;
  }

  export { simulateSinglePositionStrategy }
